/**
 * 给定一个树，每次询问[x, a, b]，即x子树内深度在[a, b]之间的节点权值的最小值、最大值、和。
 * 如果是无限制求最值或者求和，都很容易，只需要使用全局变量即可
 * 现在加上了深度限制。注意深度限制给定的是一个连续的区间，因此是区间求和(最值)
 * 再考虑到将每一个节点的权值加入，实际上是一个单点修改，因此整个过程是一个单点修改、区间查询的过程
 * 由于要同时查询最值与和，因此使用线段树
 * 注意到权值可能零，最好采用两个参数来传递修改参数，一个表示增加权值，一个表示清零
 */
#include <bits/stdc++.h>
using namespace std;

using llt = long long;
llt const INF = 0x1F2F3F4F5F6F7F8F;
llt const NINF = -INF;

struct SegTree{ // 线段树带延迟

using llt = long long;

int N;

using value_type = array<llt, 3>; // 最小值，最大值，和
vector<value_type> data; // 线段树

using lazy_type = pair<llt, int>; 
vector<lazy_type> lazy; // 延迟标记

/// 从下往上计算信息，要变动
value_type _up_(const value_type & ls, const value_type & rs) {
    // assert(0);
    return {min(ls[0], rs[0]), max(ls[1], rs[1]), ls[2] + rs[2]};
}

/// 从上往下计算信息，要变动
void _dn_(int t, int s, int e, const lazy_type & delta) {
    // assert(0);
    if(delta.second == -1){ // 清零操作
        data[t] = value_zero();        
    }else if(delta.second == 1){ // 增加一个权值， 必然是单点操作
        data[t][0] = min(data[t][0], delta.first);
        data[t][1] = max(data[t][1], delta.first);
        data[t][2] += delta.first;
    }
    return;
}

/// 初始化，清零，不用动
void init(int n) {
    data.assign((N = n) + 1 << 2, value_zero());
    // lazy.assign(n + 1 << 2, lazy_zero());
}

/// 这个函数不用动
void modify(int a, int b, const lazy_type & delta){
    _modify(1, 1, N, a, b, delta);
}

/// 这个函数不用动
value_type query(int a, int b){
    return _query(1, 1, N, a, b);
}

/// 几乎不用动
value_type _query(int t, int s, int e, int a, int b) {
    if(a <= s and e <= b) {
        return data[t];
    }
    // _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    value_type ans = value_zero(); // 如果求最值，这里不能用zero
    if(a <= mid) ans = _up_(ans, _query(lson(t), s, mid, a, b));
    if(mid < b) ans = _up_(ans, _query(rson(t), mid + 1, e, a, b));
    return ans;
}

/// 几乎不用动
void _modify(int t, int s, int e, int a, int b, const lazy_type & delta) {
    if(a <= s and e <= b) {
        _dn_(t, s, e, delta);
        return;
    }
    // _pushDown(t, s, e);
    int mid = (s + e) >> 1;
    if(a <= mid) _modify(lson(t), s, mid, a, b, delta);
    if(mid < b) _modify(rson(t), mid + 1, e, a, b, delta);
    _pushUp(t);
    return;
}

/// 这个函数不用动
void _pushUp(int t) {
    data[t] = _up_(data[lson(t)], data[rson(t)]);
}

/// 辅助函数，视延迟的类型而变动
static const lazy_type & lazy_zero() {
    static const lazy_type LAZY0 = {0, 0};
    return LAZY0; 
}

/// 辅助函数，视线段树信息类型而变动
static const value_type & value_zero() {
    static const value_type VALUE0 = {INF, NINF, 0LL};
    return VALUE0;
}

/// 这两个函数不用变动
static int lson(int x) {return x << 1;}
static int rson(int x) {return lson(x) | 1;}


};


struct dsu_on_tree_t{

using vi = vector<int>;

int N;
vector<vi> G; // 树, 1-index

struct node_t{ // 树链剖分的结构
    int size; 
    int hson; // 重儿子，这里是原树编号    
    int nid;  // 在树链剖分中的新编号
    int mdes; // 本子树全部在[nid, mdes]之间, 这是剖分编号
};
vector<node_t> Nodes;
vi New2Old;   // 剖分的编号为i，则原树节点编号为New2Old[i], 显然有Nodes[New2Old[i]].nid == i
int TimeStamp;
int Root;
vector<int> Depth;
SegTree St;
vector<long long> W;

vector<array<llt, 3>> Ans;
vector<vector<array<int, 3>>> Questions;

void init(int n){
    N = n;
    G.assign(N + 1, {});
    Nodes.assign(N + 1, {0, 0, 0, 0});
    New2Old.assign(N + 1, 0);
    TimeStamp = 0;
    Depth.assign(N + 1, 0);
    W.assign(N + 1, 0);
    St.init(N);
}

void mkDiEdge(int a, int b){
    G[a].push_back(b);
}

void mkBiEdge(int a, int b){
    mkDiEdge(a, b);
    mkDiEdge(b, a);
}

void dfsHeavy(int u, int p){ // 递归重儿子
    Depth[u] = Depth[p] + 1;
    auto & n = Nodes[u];
    n.size = 1;
    New2Old[n.nid = ++TimeStamp] = u;

    for(auto v : G[u]){
        if(v == p) continue;

        dfsHeavy(v, u);
        n.size += Nodes[v].size;
        if(Nodes[n.hson].size < Nodes[v].size) n.hson = v;
    }

    n.mdes = TimeStamp;
    return;
}


void dfs(int u, int p, bool keep){ // 递归
    const auto & n = Nodes[u];
    for(auto v : G[u]){
        if(v == p or v == n.hson) continue;
        dfs(v, u, false);
    }
    /// 最后递归重儿子
    if(n.hson) dfs(n.hson, u, true);

    /// 以下为统计u节点及其轻儿子
    if(n.hson){
        for(int i=n.nid;i<Nodes[n.hson].nid;++i){
            St.modify(Depth[New2Old[i]], Depth[New2Old[i]], {W[New2Old[i]], 1});
        }
        for(int i=Nodes[n.hson].mdes+1;i<=n.mdes;++i){
            St.modify(Depth[New2Old[i]], Depth[New2Old[i]], {W[New2Old[i]], 1});
        }
    }else{ // 只有一个节点
        St.modify(Depth[u], Depth[u], {W[u], 1});
    }

    /// 此时可以回答问题
    for(const auto & pp : Questions[u]){
        Ans[pp[2]] = St.query(Depth[u] + pp[0], Depth[u] + pp[1]);
    }

    /// 是否清空u子树对即时数据的影响
    if(not keep){
        /// 清空
        for(int i=n.nid;i<=n.mdes;++i){
            St.modify(Depth[New2Old[i]], Depth[New2Old[i]], {0LL, -1});
        }
    }
    return;
}


}Tree;

int main(){
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int n; cin >> n;

    Tree.init(n);   
    for(int i=1;i<=n;++i) cin >> Tree.W[i];
    for(int a,b,i=1;i<n;++i){
        cin >> a >> b;
        Tree.mkBiEdge(a, b);
    }
    
   
    int q; cin >> q;
    Tree.Questions.assign(n + 1, {});
    Tree.Ans.assign(q, {});
    for(int u,a,b,i=0;i<q;++i){
        cin >> u >> a >> b;
        Tree.Questions[u].push_back({a, b, i});
    }
    
    Tree.dfsHeavy(1, 0);
    Tree.dfs(1, 0, true);
    for(const auto & i : Tree.Ans) cout << i[0] << " " << i[1] << " " << i[2] << "\n";
    return 0;
}
